
#include "spi.h"
#include "fbtft.h"

#define pr_fmt(fmt) "fbtft_device: " fmt

#define CONFIG_SPI_LCM_BUS	1
#define CONFIG_SPI_LCM_CS 	0
#define CONFIG_SPI_LCM_HZ 	50000000

static struct fbtft_par *g_fbtft_par = NULL;
static struct spi_slave *g_spi_slave = NULL;


const char hex_asc[] = "0123456789abcdef";
const char hex_asc_upper[] = "0123456789ABCDEF";

extern const char hex_asc[];
#define hex_asc_lo(x)	hex_asc[((x) & 0x0f)]
#define hex_asc_hi(x)	hex_asc[((x) & 0xf0) >> 4]

static inline char *hex_byte_pack(char *buf, u8 byte)
{
	*buf++ = hex_asc_hi(byte);
	*buf++ = hex_asc_lo(byte);
	return buf;
}

extern const char hex_asc_upper[];
#define hex_asc_upper_lo(x)	hex_asc_upper[((x) & 0x0f)]
#define hex_asc_upper_hi(x)	hex_asc_upper[((x) & 0xf0) >> 4]
/*
 * NOTE! This ctype does not handle EOF like the standard C
 * library is required to.
 */

#define _U	0x01	/* upper */
#define _L	0x02	/* lower */
#define _D	0x04	/* digit */
#define _C	0x08	/* cntrl */
#define _P	0x10	/* punct */
#define _S	0x20	/* white space (space/lf/tab) */
#define _X	0x40	/* hex digit */
#define _SP	0x80	/* hard space (0x20) */

extern const unsigned char _ctype[];

#define __ismask(x) (_ctype[(int)(unsigned char)(x)])

#define isalnum(c)	((__ismask(c)&(_U|_L|_D)) != 0)
#define isalpha(c)	((__ismask(c)&(_U|_L)) != 0)
#define iscntrl(c)	((__ismask(c)&(_C)) != 0)
static inline int isdigit(int c)
{
	return '0' <= c && c <= '9';
}
#define isgraph(c)	((__ismask(c)&(_P|_U|_L|_D)) != 0)
#define islower(c)	((__ismask(c)&(_L)) != 0)
#define isprint(c)	((__ismask(c)&(_P|_U|_L|_D|_SP)) != 0)
#define ispunct(c)	((__ismask(c)&(_P)) != 0)
/* Note: isspace() must return false for %NUL-terminator */
#define isspace(c)	((__ismask(c)&(_S)) != 0)
#define isupper(c)	((__ismask(c)&(_U)) != 0)
#define isxdigit(c)	((__ismask(c)&(_D|_X)) != 0)

#define isascii(c) (((unsigned char)(c))<=0x7f)
#define toascii(c) (((unsigned char)(c))&0x7f)

static inline unsigned char __tolower(unsigned char c)
{
	if (isupper(c))
		c -= 'A'-'a';
	return c;
}

static inline unsigned char __toupper(unsigned char c)
{
	if (islower(c))
		c -= 'a'-'A';
	return c;
}

#define tolower(c) __tolower(c)
#define toupper(c) __toupper(c)

unsigned int get_unaligned(void *ptr) {
    return *(unsigned int *)ptr;
}
/*
 * Fast implementation of tolower() for internal usage. Do not use in your
 * code.
 */
static inline char _tolower(const char c)
{
	return c | 0x20;
}

/* Fast check for octal digit */
static inline int isodigit(const char c)
{
	return c >= '0' && c <= '7';
}
/**
 * is_power_of_2() - check if a value is a power of two
 * @n: the value to check
 *
 * Determine whether some value is a power of two, where zero is
 * *not* considered a power of two.
 * Return: true if @n is a power of 2, otherwise false.
 */
static inline __attribute__((const))
bool is_power_of_2(unsigned long n)
{
	return (n != 0 && ((n & (n - 1)) == 0));
}

int hex_dump_to_buffer(const void *buf, size_t len, int rowsize, int groupsize,
		       char *linebuf, size_t linebuflen, bool ascii)
{
	const u8 *ptr = buf;
	int ngroups;
	u8 ch;
	int j, lx = 0;
	int ascii_column;
	int ret;

	if (rowsize != 16 && rowsize != 32)
		rowsize = 16;

	if (len > rowsize)		/* limit to one line at a time */
		len = rowsize;
	if (!is_power_of_2(groupsize) || groupsize > 8)
		groupsize = 1;
	if ((len % groupsize) != 0)	/* no mixed size output */
		groupsize = 1;

	ngroups = len / groupsize;
	ascii_column = rowsize * 2 + rowsize / groupsize + 1;

	if (!linebuflen)
		goto overflow1;

	if (!len)
		goto nil;

	if (groupsize == 8) {
		const u64 *ptr8 = buf;

		for (j = 0; j < ngroups; j++) {
			ret = snprintf(linebuf + lx, linebuflen - lx,
				       "%s%16.16llx", j ? " " : "",
				       get_unaligned(ptr8 + j));
			if (ret >= linebuflen - lx)
				goto overflow1;
			lx += ret;
		}
	} else if (groupsize == 4) {
		const u32 *ptr4 = buf;

		for (j = 0; j < ngroups; j++) {
			ret = snprintf(linebuf + lx, linebuflen - lx,
				       "%s%8.8x", j ? " " : "",
				       get_unaligned(ptr4 + j));
			if (ret >= linebuflen - lx)
				goto overflow1;
			lx += ret;
		}
	} else if (groupsize == 2) {
		const u16 *ptr2 = buf;

		for (j = 0; j < ngroups; j++) {
			ret = snprintf(linebuf + lx, linebuflen - lx,
				       "%s%4.4x", j ? " " : "",
				       get_unaligned(ptr2 + j));
			if (ret >= linebuflen - lx)
				goto overflow1;
			lx += ret;
		}
	} else {
		for (j = 0; j < len; j++) {
			if (linebuflen < lx + 2)
				goto overflow2;
			ch = ptr[j];
			linebuf[lx++] = hex_asc_hi(ch);
			if (linebuflen < lx + 2)
				goto overflow2;
			linebuf[lx++] = hex_asc_lo(ch);
			if (linebuflen < lx + 2)
				goto overflow2;
			linebuf[lx++] = ' ';
		}
		if (j)
			lx--;
	}
	if (!ascii)
		goto nil;

	while (lx < ascii_column) {
		if (linebuflen < lx + 2)
			goto overflow2;
		linebuf[lx++] = ' ';
	}
	for (j = 0; j < len; j++) {
		if (linebuflen < lx + 2)
			goto overflow2;
		ch = ptr[j];
		linebuf[lx++] = (isascii(ch) && isprint(ch)) ? ch : '.';
	}
nil:
	linebuf[lx] = '\0';
	return lx;
overflow2:
	linebuf[lx++] = '\0';
overflow1:
	return ascii ? ascii_column + len : (groupsize * 2 + 1) * ngroups - 1;
}

void fbtft_dbg_hex(int groupsize, void *buf, size_t len, const char *fmt, ...)
{
	va_list args;
	static char textbuf[512];
	char *text = textbuf;
	size_t text_len;

	va_start(args, fmt);
	text_len = vscnprintf(text, sizeof(textbuf), fmt, args);
	va_end(args);

	hex_dump_to_buffer(buf, len, 32, groupsize, text + text_len,
				512 - text_len, false);

	if (len > 32)
		printf("FBTFT: %s ...\n", text);
	else
		printf("FBTFT: %s\n", text);
}

int fbtft_write_spi(struct fbtft_par *par, void *buf, size_t len)
{
	unsigned long flags = SPI_XFER_BEGIN;
	int rtn = spi_xfer(g_spi_slave, 8*len, buf, NULL, flags);
	if (rtn) {
		printf("FBTFT: Failed to send cmd %s: %d\n", buf, rtn);
	}
	return rtn;
}

int fbtft_read_spi(struct fbtft_par *par, void *buf, size_t len)
{
	unsigned long flags = SPI_XFER_BEGIN;
	int rtn = spi_xfer(g_spi_slave, 8*len, NULL, buf, flags);
	if (rtn) {
		printf("FBTFT: Failed to recv data %s: %d\n", buf, rtn);
	}
	return rtn;
}

void fbtft_write_reg8_bus8(struct fbtft_par *par, int len, ...)
{
	va_list args;
	int i, ret;
	int offset = 0;
	u8 *buf = (unsigned char *)par->buf;

	if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) {
		va_start(args, len);
		for (i = 0; i < len; i++) {
			buf[i] = (unsigned char)va_arg(args, unsigned int);
		}
		va_end(args);
		fbtft_dbg_hex(sizeof(u8), buf, (len) * sizeof(u8), "%s: ", __func__);
	}

	va_start(args, len);

	*buf = (unsigned char)va_arg(args, unsigned int);
	//printf("%s %02X ", __func__, buf[0]);
	if (par->gpio.dc != -1)
		gpio_set_value(par->gpio.dc, 0);
	ret = par->fbtftops.write(par, par->buf, sizeof(unsigned char) + offset);
	if (ret < 0) {
		va_end(args);
		printf("FBTFT: %s: write() failed and returned %d\n", __func__, ret);
		return;
	}
	len--;

	if (len) {
		i = len;
		while (i--) {
			*buf++ = ((unsigned char)va_arg(args, unsigned int));
			//printf("%02X ", buf[i]);
		}
		if (par->gpio.dc != -1)
			gpio_set_value(par->gpio.dc, 1);
		ret = par->fbtftops.write(par, par->buf, len * (sizeof(u8) + offset));
		if (ret < 0) {
			va_end(args);
			printf("FBTFT: %s: write() failed and returned %d\n", __func__, ret);
			return;
		}
	}
	va_end(args);
}

void fbtft_write_dat8_bus8(struct fbtft_par *par, int len, ...)
{
	va_list args;
	int i, ret;
	int offset = 0;
	unsigned char *buf = (unsigned char *)par->buf;

	va_start(args, len);

	if (len) {
		i = len;
		while (i--) {
			*buf++ = ((unsigned char)va_arg(args, unsigned int));
		}
		if (par->gpio.dc != -1)
			gpio_set_value(par->gpio.dc, 1);
		ret = par->fbtftops.write(par, par->buf, len * (sizeof(u8) + offset));
		if (ret < 0) {
			va_end(args);
			printf("FBTFT: %s: write() failed and returned %d\n", __func__, ret);
			return;
		}
	}
	va_end(args);
}


void fbtft_write_datas_bus8(struct fbtft_par *par, const unsigned char *pbuf, int len) {
    int ret;
    if (len) {
        if (par->gpio.dc!= -1)
            gpio_set_value(par->gpio.dc, 1);
        ret = par->fbtftops.write(par, pbuf, len);
        if (ret < 0) {
            printf("FBTFT: %s: write() failed and returned %d\n", __func__, ret);
            return;
        }
    }
}

extern void do_logo_disp_datas(void *_par );

int fbtft_probe_common(struct fbtft_display *display)
{
	struct fbtft_par *par = (struct fbtft_par *)malloc(sizeof(struct fbtft_par));
	if(!par) {
		printf("FBTFT: %s struct fbtft_par malloc failed.\n", __func__);
		return -1;
	}
	memset(par, 0, sizeof(struct fbtft_par));

	par->buf = (u8 *)malloc(128);
	if(!par->buf) {
		printf("FBTFT: %s fbtft_par->buf malloc failed.\n", __func__);
		return -1;
	}
	memset(par->buf, 0, sizeof(128));

	/* Initialize gpios to disabled */
	par->gpio.reset = -1;
	par->gpio.dc = -1;
	par->gpio.rd = -1;
	par->gpio.wr = -1;
	par->gpio.cs = -1;
	par->gpio.latch = -1;
	for (int i = 0; i < 16; i++) {
		par->gpio.db[i] = -1;
		par->gpio.led[i] = -1;
		par->gpio.aux[i] = -1;
	}
	/* default fbtft operations */
	par->fbtftops.write = fbtft_write_spi;
	par->fbtftops.read = fbtft_read_spi;
	par->fbtftops.write_register = fbtft_write_reg8_bus8;
	par->fbtftops.request_gpios = display->fbtftops.request_gpios;
	par->fbtftops.init_display = display->fbtftops.init_display;

	par->debug = display->debug;

	par->fbtftops.request_gpios(par);
	par->fbtftops.init_display(par);
	#if 0
	extern void do_logo_disp_data(void *, u16 *);
	do_logo_disp_data(par, NULL);
	extern do_logo_disp_color(struct fbtft_par *par, u16);
	// do_logo_disp_color(par, 0xF800);
	for(int i=0; i<10; i++) udelay(1000000);
	#else
	//display logo
	do_logo_disp_datas(par);
	#endif

	g_fbtft_par = par;
	return 0;
}

int spi_lcm_init(void)
{
	struct spi_slave *spi;
	int ret;
	unsigned int val;

	/* system enable spi0 */
	writel(2, 0x09200250);	//100MHz spi0 clk src
	val = readl(0x0c0000ac);
	writel(val | (1 << 6), 0x0c0000ac);	//spi0 clock enable
	val = readl(0x0c0000b0);
	writel(val | (1 << 8), 0x0c0000b0);	//spi0 apb clock enable

	spi = spi_setup_slave(1, CONFIG_SPI_LCM_CS, CONFIG_SPI_LCM_HZ, SPI_MODE_3);
	if (!spi)
	{
		printf("FBTFT: %s SPI LCM Failed to set up slave\n", __func__);
		return -1;
	}
	g_spi_slave = spi;

	ret = spi_claim_bus(spi);
	if (ret)
	{
		printf("FBTFT: %s SPI LCM Failed to claim SPI bus: %d\n", __func__, ret);
		goto __err_bus;
	}
	ret = fbtft_display_init();
	if (0 == ret) {
		return 0;
	}

__err_bus:
	spi_free_slave(spi);
	printf("FBTFT: %s err\n", __func__);
	return -1;
}

#if 0
	.buswidth = 8,
	.backlight = 1,
	.fps = 30,
	.max_speed_hz = 50000000,
	.mode = SPI_MODE_0,
#endif
